package ace.tool.generate.code.impl.resolver.ui.resolver;

import ace.tool.generate.code.impl.manager.common.EntityModelManager;
import ace.tool.generate.code.impl.resolver.ui.model.bo.*;
import ace.tool.generate.code.impl.resolver.ui.model.dto.UiClassFileInfo;
import ace.tool.generate.code.impl.service.model.input.GenerateInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelFieldInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelInput;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.text.StringSubstitutor;
import org.apache.commons.text.WordUtils;

/**
 * @author caspar
 * @date 2023/11/17 10:34
 */
@RequiredArgsConstructor
public class UiMockServerApiClassInfoResolver {
  private final GenerateInput input;
  private final GenerateModelInput modelInput;
  private final UiProjectCommonInfo commonInfo;
  private final ResponseClassInfo responseClassInfo;
  private final UiApiConfigClassInfo uiApiConfigClassInfo;
  private final EntityModelManager entityModelManager = EntityModelManager.getInstance();

  public UiMockServerApiClassInfo resolve() {

    String entityModelId = entityModelManager.getEntityModelId(input, modelInput);

    UiClassFileInfo classFileInfo =
        UiClassFileInfoResolver.builder()
            .className(modelInput.getName())
            .classNameSuffix(input.getUiMockServerApiClassNameSuffix())
            .classPackagePrefix(input.getUiMockServerApiClassPackagePrefix())
            .rootPackageFullName(
                input.getUiMockServerApiClassRootPackageFullNamePrefix()
                    + "/"
                    + commonInfo.getProjectModuleCodeName())
            .sourceCodeRootDir(commonInfo.getSrcDir())
            .fileNameSuffix(".ts")
            .build()
            .resolve();

    String apiConfigFullPath = this.getApiConfigFullPath(classFileInfo);
    return UiMockServerApiClassInfo.builder()
        .info(classFileInfo)
        .entityModelId(entityModelId)
        .modelRemark(modelInput.getRemark())
        .fields(this.getFields(entityModelId, classFileInfo))
        .responseClassInfo(responseClassInfo)
        .apiConfigFullPath(apiConfigFullPath)
        .adminApiConfigClassName(uiApiConfigClassInfo.getInfo().getUncapitalizeClassName())
        .adminApiConfigClassFullName(uiApiConfigClassInfo.getInfo().getUncapitalizeClassFullName())
        .build();
  }

  private String getApiConfigFullPath(UiClassFileInfo classFileInfo) {
    Map<String, String> model = new HashMap<>();
    model.put("moduleName", uiApiConfigClassInfo.getInfo().getUncapitalizeClassName());
    model.put("modelName", WordUtils.uncapitalize(modelInput.getName()));
    String apiConfigFullPath = StringSubstitutor.replace("${moduleName}.${modelName}", model);
    return apiConfigFullPath;
  }

  private List<UiMockServerApiFieldInfo> getFields(String entityModelId, UiClassFileInfo info) {
    List<UiMockServerApiFieldInfo> fields =
        modelInput.getFields().stream().map(p -> this.getField(p)).collect(Collectors.toList());

    if (this.isNeedAddDeleteFlag(fields)) {
      fields.add(this.getDefaultDeleteField());
    }
    if (input.getEnableCreateTimeField()) {
      fields.add(this.getDefaultCreateTimeField());
    }
    if (input.getEnableUpdateTimeField()) {
      fields.add(this.getDefaultUpdateTimeField());
    }
    if (input.getEnableRowVersionField()) {
      fields.add(this.getDefaultRowVersionField());
    }
    return fields;
  }

  private boolean isNeedAddDeleteFlag(List<UiMockServerApiFieldInfo> fields) {
    boolean isExitsDeleteFlagField =
        fields.stream().anyMatch(p -> p.getName().equals(input.getDeleteFlagFieldName()));
    return input.getEnableDeleteFlagField() && !isExitsDeleteFlagField;
  }

  private UiMockServerApiFieldInfo getDefaultRowVersionField() {
    return UiMockServerApiFieldInfo.builder()
        .name(input.getRowVersionFieldName())
        .type(this.getMapType(Integer.class))
        .nullable(false)
        .value("1")
        .build();
  }

  private UiMockServerApiFieldInfo getDefaultUpdateTimeField() {
    return UiMockServerApiFieldInfo.builder()
        .name(input.getUpdateTimeFieldName())
        .type(this.getMapType(Long.class))
        .nullable(false)
        .value("new Date().getTime().toString()")
        .build();
  }

  private UiMockServerApiFieldInfo getDefaultCreateTimeField() {
    return UiMockServerApiFieldInfo.builder()
        .name(input.getCreateTimeFieldName())
        .type(this.getMapType(Long.class))
        .nullable(false)
        .value("new Date().getTime().toString()")
        .build();
  }

  private UiMockServerApiFieldInfo getDefaultDeleteField() {
    return UiMockServerApiFieldInfo.builder()
        .name(input.getDeleteFlagFieldName())
        .type(this.getMapType(Long.class))
        .nullable(false)
        .value("'0'")
        .build();
  }

  private String getMapType(Class<?> cls) {
    return new UiTypeResolver(cls).resolve();
  }

  private UiMockServerApiFieldInfo getField(GenerateModelFieldInput modelFieldInput) {

    String name = modelFieldInput.getName();
    String type = this.getMapType(modelFieldInput.getType());
    return UiMockServerApiFieldInfo.builder()
        .name(name)
        .type(type)
        .nullable(modelFieldInput.getNullable())
        .value(this.getMapValue(name, type, modelFieldInput.getNullable()))
        .build();
  }

  private String getMapValue(String name, String type, boolean nullable) {
    String value = "i.toString()";
    if ("boolean".equals(type)) {
      value = "(Math.floor(Math.random() * 2) + 1) === 1";
    }
    if (name.equals("createTime") || name.equals("updateTime")) {
      value = "new Date().getTime().toString()";
    } else if (type.equals("number")) {
      value = "i";
    } else if (name.contains("id") || name.contains("Id")) {
      value = "i.toString()";
    } else if (type.equals("string")) {
      value = "`" + name + "_" + this.modelInput.getRemark() + "_${i.toString()}`";
    }
    String template = "${value}";
    if (nullable) {
      template = "Mock.Random.integer(1, 2) == 1 ? null : ${value}";
    }
    Map<String, String> model = new HashMap<>(2);
    model.put("value", value);
    String result = StringSubstitutor.replace(template, model);
    return result;
  }
}
